/**
 * 
 */
package ro.dta.idbi.patterns.primitives;

import java.util.EnumSet;
import java.util.List;

import ro.dta.idbi.interfaces.IntegrationOperation;
import ro.dta.idbi.model.AbstractPrimitivePattern;
import ro.dta.idbi.model.Utils;
import uk.ac.ic.doc.automed.IntegrityException;
import uk.ac.ic.doc.automed.NotFoundException;
import uk.ac.ic.doc.automed.reps.Schema;
import uk.ac.ic.doc.automed.reps.SchemaObject;
import uk.ac.ic.doc.automed.reps.TypeMismatchException;

/**
 * <b>Primitive transformation to add a SQL construct</b><br />
 * 
 * Example of use:<br />
 * 
 * <code>add (table:&lt;&lt;tableName&gt;&gt;, iql)</code><br />
 * <code>add (column:&lt;&lt;tableName,columnName,optionality,type&gt;&gt;, iql)</code>
 * <br />
 * <code>add (primarykey:&lt;&lt;pk_name,table,&lt;&lt;table,col1&gt;&gt;, ..., &lt;&lt;table,coln&gt;&gt;&gt;&gt;)</code>
 * <br />
 * <code>add (foreignkey:&lt;&lt;fk_name,sourceTbl,&lt;&lt;sourceTbl,col1&gt;&gt;, ..., &lt;&lt;sourceTbl,coln&gt;&gt;,targetTbl,&lt;&lt;targetTbl,col1&gt;&gt;, ..., &lt;&lt;targetTbl,coln&gt;&gt;&gt;&gt;)</code>
 * 
 * @author Tudor Dobrila
 * 
 */
public class AddPrimitive extends AbstractPrimitivePattern {

	/**
	 * Name of the pattern
	 */
	public static final String NAME = "Add new construct";

	/**
	 * Description of the pattern
	 */
	public static final String DESCRIPTION = "Introduce a new construct";

	/**
	 * Name of the command associated with the pattern
	 */
	public static final String COMMAND = "add";

	/**
	 * Integration phase during which the command is applied
	 */
	public static final EnumSet<IntegrationOperation> OPERATION = EnumSet.of(
			IntegrationOperation.CONFORMING, IntegrationOperation.MERGING,
			IntegrationOperation.IMPROVEMENT);

	/**
	 * Add a new construct class constructor
	 * 
	 * @param sourceSchema
	 *            Source schema over which the transformation is applied
	 */
	public AddPrimitive(final Schema sourceSchema) throws NotFoundException, IntegrityException {
		super(sourceSchema, COMMAND);
	}

	/**
	 * Add a new construct class constructor
	 * 
	 * @param sourceSchema
	 *            Name of source schema over which the transformation is applied
	 */
	public AddPrimitive(final String sourceSchema) throws NotFoundException, IntegrityException {
		this(Schema.getSchema(sourceSchema));
	}

	@Override
	public Schema execute(String trans) throws NotFoundException, TypeMismatchException,
			IntegrityException {
		super.execute(trans);

		Schema crtSchema = sourceSchema;

		String type = (String) construct.get("type");
		if ("table".equals(type)) {
			String name = (String) construct.get("name");
			String extent = (String) construct.get("iql");
			if (!crtSchema.contains("<<" + name + ">>")) {
				crtSchema = crtSchema.applyAddTransformation(table, new Object[] { name }, extent,
						null);
			}
		} else if ("column".equals(type)) {
			String table = (String) construct.get("table");
			String name = (String) construct.get("name");
			String nullable = (String) construct.get("nullable");
			String coltype = (String) construct.get("coltype");
			String extent = (String) construct.get("iql");
			SchemaObject tableObj = crtSchema.getSchemaObject("<<" + table + ">>");
			Object[] colDef = new Object[4];
			colDef[0] = tableObj;
			colDef[1] = name;
			colDef[2] = nullable;
			colDef[3] = coltype;
			if (!crtSchema.contains("<<" + table + "," + name + ">>")) {
				crtSchema = crtSchema.applyAddTransformation(column, colDef, extent, null);
			}
		} else if ("primarykey".equals(type)) {
			String name = (String) construct.get("name");
			String table = (String) construct.get("table");
			@SuppressWarnings("unchecked")
			List<String> cols = (List<String>) construct.get("cols");
			SchemaObject tableObj = crtSchema.getSchemaObject("<<" + table + ">>");
			Object[] pkArr = new Object[2 + cols.size()];
			pkArr[0] = name;
			pkArr[1] = tableObj;
			for (int i = 0; i < cols.size(); i++) {
				SchemaObject crtCol = crtSchema.getSchemaObject(cols.get(i));
				pkArr[i + 2] = crtCol;
			}
			String pk = Utils.genPKRepresentation(pkArr);
			pk = pk.substring(11, pk.length());
			if (!crtSchema.contains(pk)) {
				crtSchema = crtSchema.applyAddTransformation(primaryKey, pkArr, null, null);
			}
		} else if ("foreignkey".equals(type)) {
			String name = (String) construct.get("name");
			String sourceTbl = (String) construct.get("sourcetable");
			String targetTbl = (String) construct.get("targettable");
			@SuppressWarnings("unchecked")
			List<String> sourceCols = (List<String>) construct.get("sourcecols");
			@SuppressWarnings("unchecked")
			List<String> targetCols = (List<String>) construct.get("targetcols");
			if (sourceCols.size() != targetCols.size()) {
				throw new IllegalArgumentException("Invalid command");
			}

			SchemaObject sourceObj = crtSchema.getSchemaObject("<<" + sourceTbl + ">>");
			SchemaObject targetObj = crtSchema.getSchemaObject("<<" + targetTbl + ">>");
			Object[] fkDef = new Object[3 + 2 * sourceCols.size()];
			fkDef[0] = name;
			fkDef[1] = sourceObj;
			fkDef[2 + sourceCols.size()] = targetObj;
			for (int i = 0; i < sourceCols.size(); i++) {
				SchemaObject sourceCol = crtSchema.getSchemaObject(sourceCols.get(i));
				fkDef[i + 2] = sourceCol;

				SchemaObject targetCol = crtSchema.getSchemaObject(targetCols.get(i));
				fkDef[i + 3 + sourceCols.size()] = targetCol;
			}
			
			if (!crtSchema.contains((String) construct.get("fulldef"))) {
				crtSchema = crtSchema.applyAddTransformation(foreignKey, fkDef, null, null);
			}
		}

		return crtSchema;
	}
}
